///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_MAT_DETAIL_CELEMENTBINARYOP_H_
#define _BUOLA_MAT_DETAIL_CELEMENTBINARYOP_H_

#include <buola/mat/cexpr.h>

namespace buola { namespace mat { namespace detail {

template<typename tLH,typename tRH,typename tOp>
struct MMatrixTraits<CElementBinaryOp<tLH,tRH,tOp>>
{
private:
    typedef MMatrixTraits<ODecay<tLH>> TLHTraits;
    typedef MMatrixTraits<ODecay<tRH>> TRHTraits;
    static_assert(TLHTraits::cRows==cUnknown||TRHTraits::cRows==cUnknown||
                  (TLHTraits::cRows==cDynamic)||//&&TRHTraits::cRows!=1)||
                  (TRHTraits::cRows==cDynamic)||//&&TLHTraits::cRows!=1)||
                  TLHTraits::cRows==TRHTraits::cRows,"wrong number of rows in binary operation");
    static_assert(TLHTraits::cCols==cUnknown||TRHTraits::cCols==cUnknown||
                  (TLHTraits::cCols==cDynamic)||//&&TRHTraits::cCols!=1)||
                  (TRHTraits::cCols==cDynamic)||//&&TLHTraits::cCols!=1)||
                  TLHTraits::cCols==TRHTraits::cCols,"wrong number of cols in binary operation");
    
public:
    typedef decltype(std::declval<tOp>()(std::declval<OScalar<tLH>>(),std::declval<OScalar<tRH>>())) TScalar;
    static const size_t cRows=TLHTraits::cRows!=cDynamic?TLHTraits::cRows:TRHTraits::cRows;
    static const size_t cCols=TLHTraits::cCols!=cDynamic?TLHTraits::cCols:TRHTraits::cCols;
    static const bool cRowLinearAccess=TLHTraits::cRowLinearAccess&&TRHTraits::cRowLinearAccess;
    static const bool cColLinearAccess=TLHTraits::cColLinearAccess&&TRHTraits::cColLinearAccess;
    static const bool cRowPacketAccess=TLHTraits::cRowPacketAccess&&TRHTraits::cRowPacketAccess;
    static const bool cColPacketAccess=TLHTraits::cColPacketAccess&&TRHTraits::cColPacketAccess;
    static const bool cMightAlias=(QIsReference<tLH>::value&&TLHTraits::cMightAlias)||
                                  (QIsReference<tRH>::value&&TRHTraits::cMightAlias);
    static const int cElementAccess=constexpr_min(constexpr_min(TLHTraits::cElementAccess,TRHTraits::cElementAccess),
                                                  (int)ACCESS_SLOW);
    static const bool cEigenMappable=false;
    static const bool cKeepConstness=false;
    static const bool cExprAssignable=false;
    static const bool cIsMatrix=false;
};

//totally fixed size version
template<typename tLH,typename tRH,typename tOp>
class CElementBinaryOp : public CExpr<CElementBinaryOp<tLH,tRH,tOp>>
{
    typedef MMatrixTraits<CElementBinaryOp<tLH,tRH,tOp>> TSelfTraits;
    
public:
    CElementBinaryOp(tLH pLH,tRH pRH,tOp pOp)
        :   mLH(pLH)
        ,   mRH(pRH)
        ,   mOp(pOp)
    {
        assert(pLH.Rows()==pRH.Rows()&&pLH.Cols()==pRH.Cols());
    }

    CElementBinaryOp(const CElementBinaryOp&)=delete;
    CElementBinaryOp& operator=(const CElementBinaryOp&)=delete;

    size_t Rows() const  {   return mLH.Rows();  }
    size_t Cols() const  {   return mLH.Cols();  }
    size_t size() const  {   return mLH.size();  }

    template<typename tType>
    tType At(size_t r,size_t c) const
    {
        return mOp(mLH.template At<OScalar<tLH>>(r,c),mRH.template At<OScalar<tRH>>(r,c));
    }

    template<typename tType>
    tType At(size_t i) const
    {
        return mOp(mLH.template At<OScalar<tLH>>(i),mRH.template At<OScalar<tRH>>(i));
    }

    template<bool b=std::is_same<typename TSelfTraits::TScalar,bool>::value,typename std::enable_if<b,int>::type=0>
    explicit operator bool()
    {
        const auto &lLH=element_accessible<ACCESS_ONCE>(mLH);
        const auto &lRH=element_accessible<ACCESS_ONCE>(mRH);
        
        int lRows=mLH.Rows();
        int lCols=mLH.Cols();
        
        for(int r=0;r<lRows;r++)
        {
            for(int c=0;c<lCols;c++)
            {
                if(!mOp(lLH.template At<OScalar<tLH>>(r,c),lRH.template At<OScalar<tRH>>(r,c)))
                    return false;
            }
        }

        return true;
    }
    
    
private:
    tLH mLH;
    tRH mRH;
    tOp mOp;
};

template<typename tLH,typename tRH,typename tOp>
using OElementBinaryOpResult=CElementBinaryOp<OElementAccessible<tLH,ACCESS_ONCE>,OElementAccessible<tRH,ACCESS_ONCE>,tOp>;

/*namespace detail*/ } /*namespace mat*/ } /*namespace buola*/ }

#endif
